const defaultConfig = require('./defaultConfig');
const createPainationControllor = require('./pagination');
const compiler = require('./compiler');

/* Speaker
—————————————————————————————————————————— */
class Speaker {
  constructor (config) {
    // Initial
    this.initOptions(config);
    this.init();

    // Bind Events
    this.container.addEventListener('transitionend', () => {
      // Open Lock
      this.__canJumpPage = true;
    }, false);

    this.container.addEventListener('click', (e) => {
      if (e.target.tagName.toUpperCase() === 'A') { return; }
      if (!this.__canJumpPage) { return; }
      this.next.bind(this)();
    }, false);

    if (this.options.keyboard) {
      document.addEventListener('keydown', (e) => {
        if (!this.__canJumpPage) { return; }
        switch (e.which) {
          case 37:
          case 38:
            this.prev.bind(this)();
            break;
          case 13:
          case 32:
          case 39:
          case 40:
            this.next.bind(this)();
            break;
        }
      }, false);
    }

    if (this.options.mouse) {
      // set initial value;
      this.__timeStamp = Date.now();
      this.__speed = 0;

      /** On the laptop, a problem is "natural rolling" of the touch pad **/
      /** Solution:                                                      **/
      /** 1. set a time difference between the "mousewheel event"        **/
      /** 2. set a "acceleration" of the page scrolling                  **/
      /**                                                                **/
      /** Here is the appropriate value I tested                         **/
      const timeDifferece = this.options.duration + 500;
      const acceleration = 50;

      this.container.addEventListener('mousewheel', (e) => {
        if (!this.__canJumpPage) { return; }
        // get this frame value
        const current = Date.now();
        const speed = Math.abs(e.wheelDeltaY);
        // calculate delta
        const timeDelta = current - this.__timeStamp;
        const speedDelta = Math.abs(e.wheelDeltaY) - this.__speed;
        // save this frame value
        this.__timeStamp = current;
        this.__speed = speed;

        if (timeDelta < timeDifferece && speedDelta < acceleration) { return; }
        if (e.wheelDeltaY < 0) {
          this.next.bind(this)();
        } else {
          this.prev.bind(this)();
        }
      }, false);
    }

    // Pagination Controllor
    if (this.options.controllor) {
      this.pagination = createPainationControllor(this.currentIndex, this.pageCount);
      this.pagination.addEventListener('click', (e) => {
        if (!this.__canJumpPage) { return; }
        if (e.target.tagName.toUpperCase() === 'LI') {
          this.currentIndex = +e.target.getAttribute('data-index');
        }
      }, false);
    } else {
      this.pagination = null;
    }

    // Compile
    this.compile();

    // Jump to the  initial page
    this.jumpPage();
  }

  // Initial
  initOptions (config) {
    // this.options = Object.assign({}, config);

    this.options = {};
    for (let key in defaultConfig) {
      if(config[key] === undefined) {
        this.options[key] = defaultConfig[key];
      } else {
        this.options[key] = config[key];
      }
    }
  }

  init () {
    // Calculate Basic Information
    this.app = document.querySelector('#speaker');
    this.container = this.app.children[0];
    this.pageList = this.container.children;

    this.pageCount = this.pageList.length;

    // Set Direction
    if (this.options.direction === 'horizontal') {
      this.container.style.width = this.pageCount * 100 + '%';
      for (let page of this.pageList) {
        page.style.float = 'left';
        page.style.width = (100 / this.pageCount).toFixed(4) + '%';
      }
    }

    /** Bind Function on currentIndex **/
    let __cindex = this.options.index;
    Object.defineProperty(this, 'currentIndex', {
      get () {
        return __cindex;
      },
      set (newVal) {
        if (__cindex !== newVal) {
          if (newVal < 0) {
            __cindex = this.options.loop ? this.pageCount - 1 : 0;
          } else if (newVal >= this.pageCount) {
            __cindex = this.options.loop ? 0 : this.pageCount - 1;
          } else {
            __cindex = newVal;
          }
          this.jumpPage(__cindex);
          this.changePagination(__cindex);
        }
      }
    });

    // Get Browser Prefix
    const prefixTestElement = document.createElement('prefixTestElement');
    for (let prefix of ['webkit', 'moz', 'o', 'ms']) {
      const transitionStyle = prefixTestElement.style[prefix + 'Transition'];
      if (transitionStyle !== undefined) {
        this.__prefix = prefix;
      }
    }

    // Set Transition
    this.container.style['transition'] = `transform ${this.options.duration}ms ${this.options.animation} 0ms`;
    if (this.__prefix) {
      this.container.style[this.__prefix + 'Transition'] = `transform ${this.options.duration}ms ${this.options.animation} 0ms`;
    }

    // Lock Animation
    this.__canJumpPage = true;
  }

  // Events Handle
  prev () {
    this.currentIndex--;
  }

  next () {
    this.currentIndex++;
  }

  jumpPage (index = this.currentIndex) {
    // Lock
    this.__canJumpPage = false;

    if (this.options.direction === 'horizontal') {
      this.container.style['transform'] = `translate(${-100 * index}%, 0)`;
      if (this.__prefix) {
        this.container.style[this.__prefix + 'Transform'] = `translate(${-100 * index}%, 0)`;
      }
    } else {
      this.container.style['transform'] = `translate(0, ${-100 * index}%)`;
      if (this.__prefix) {
        this.container.style[this.__prefix + 'Transform'] = `translate(0, ${-100 * index}%)`;
      }
    }

    // Do callback
    if (typeof this.options.callback === 'function') {
      this.options.callback(index);
    }
  }

  changePagination (index = this.currentIndex) {
    if (!this.pagination) { return; }
    const lists = this.pagination.getElementsByTagName('li');

    /** BE CAREFUL !!!                                       **/
    /** document.getElementsByTagName etc. is HTMLCollection **/
    /** In IE / FF : keys like ms__id2169, ms__id2171 ...    **/
    /** In Chrome / Safari / Opera: keys is 0, 1, 2, 3 ...   **/
    let i = 0;
    for (let key in lists) {
      if (lists[key].nodeType === 1) {
        const classLists = lists[key].classList;
        if (parseInt(i) !== index) {
          classLists.remove('active');
        } else {
          classLists.add('active');
        }
      }
      i++;
    }
  }

  // Compile Markdown to HTML
  compile () {
    compiler(this.pageList);
  }
};

module.exports = Speaker;
